Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Integrate with a CSS parser for selector parsing #1086

Merged
merged 25 commits into from
Sep 16, 2017

Conversation

aweary
Copy link
Collaborator

@aweary aweary commented Aug 21, 2017

Resolves #694, #693, #547, #1082, #1019, #1078, #1086, #283

This PR integrates with rst-selector-parser a fork of scalpel which is a CSS parser implemented with nearley. This fork implements a custom grammar requiring quoted string attribute values, allowing unquoted numbers, booleans, and other primitives, and supports selectors in any order.

Major Changes

  • buildPredicate is exported by the selectors module, the API remains the same

  • ComplexSelector is no longer needed. Instead the selectors module exports a reduceTreeBySelector which manages traversal + matching with simple selectors and combinators. Both the shallow and mount wrappers use the same buildPredicate and findWhereUnwrapped API so there's no need to have a class for selector parsing.

Pending Changes

These are changes that I still need to make before this can be merged.

  • Remove ComplexSelector and all utility methods that were only used by ComplexSelector
  • Throw if a complex selector is passed to buildPredicate
  • Handle PSEUDO_CLASS and PSEUDO_ELEMENT selectors
  • Add tests for valid selectors that were previously unparsable

@@ -433,22 +435,15 @@ describeWithDOM('mount', () => {
expect(wrapper.find('.row + .row')).to.have.lengthOf(1);
});

// React 15.2 warns when setting a non valid prop to an DOM element
describeIf(REACT013 || REACT014, 'unauthorized dom props', () => {
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test was kind of a false positive. You could still query unauthorized DOM props by using the object syntax, and AFAICT it was only failing to find them because of how it was parsing the selector.

React <0.14 didn't just ignore syntactically invalid attributes, it ignored attributes that it didn't have whitelisted. To implement this behavior as it should be implemented we'd need to integrate that same whitelist.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we add that whitelist in the react 0.14 and 0.13 adapters?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, OK to do in a follow up?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, totes ok to do in a followup

Copy link
Member

@ljharb ljharb left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pausing my review pending answers about the quoted string question.

@@ -308,14 +308,6 @@ export function coercePropValue(propName, propValue) {

const trimmedValue = propValue.trim();

// if propValue includes quotes, it should be
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why? The fact that selectors can omit quotes is imo very problematic; enforcing strings ensures that selectors used will be unambiguous.

@@ -155,13 +153,6 @@ describe('RSTTraversal', () => {
expect(nodeHasProperty(<div foo={-Infinity} />, 'foo', '-Infinity')).to.equal(true);
expect(nodeHasProperty(<div foo={0} />, 'foo', '-Infinity')).to.equal(false);
});

it('should throw when un unquoted string is passed in', () => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tagging this line too - i think the purpose of requiring strings here was so people would distinguish between numbers and strings, for example.

Regardless of what mistakes React 16 has made, older Reacts do, and non-Reacts might, still want to distinguish these on host components - and they'll always need distinguishing on custom components.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

According to the spec, unquoted values must be valid identifiers or strings. The selectors [foo=bar] and [foo="bar"] are thus parsed identically. A spec-compliant parser would return identical tokens for both of those selectors, and that's indeed what scalpel does.

Using numbers as the attribute value is a violation of the spec, e.g., [foo=42], since valid CSS identifiers cannot start with numbers. Implementing this behavior would require using a selector parser that does not conform to the selector spec.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right - the problem is that we can't use spec-compliant selectors, because we're not selecting HTML - we're selecting from an RST, which has non-string values (HTML only has strings).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In other words, we can only use the actual selector spec if we're restricting selectors to DOM elements - so enzyme's selection is always going to have to be different than the web spec (we test react native too, for example)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In that case, I can just fork scalpel and redefine the grammar to require unquoted values be numerical. I'm not super familiar with writing parser grammars but I think I can do it 😄

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it'd be even better if we could make a new package that avoids forking and instead directly depends on scalpel, but i'm not sure how hard that'd be :-/

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It wouldn't really be possible, this requires making a change directly to the grammar for attribute values and there's no way to do that without changing grammar.ne.

@aweary
Copy link
Collaborator Author

aweary commented Aug 21, 2017

@ljharb here's a PR to my fork of scalpel that updates the grammar to require quoted string values and also parses unquoted numerical values: aweary/rst-selector-parser#1

I was going to publish this as rst-selector-parser and use that in this PR instead.

@aweary
Copy link
Collaborator Author

aweary commented Aug 22, 2017

@ljharb I've integrated the new rst-selector-parser package which has actually simplified things. I updated the grammar to parse all the unquoted values we support (aweary/rst-selector-parser#2, aweary/rst-selector-parser#3, aweary/rst-selector-parser#4) so the parser will return the value we want instead of a stringified version (for example, [foo=NaN] will return NaN for the token value not "NaN").
This means we no longer need to explicitly coerce prop values.

throw new TypeError(
`Enzyme::Unable to parse selector '[${propName}=${propValue}]'. ` +
`Perhaps you forgot to escape a string? Try '[${propName}="${trimmedValue}"]' instead.`,
);
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any selector that is unparsable will fail during the parsing step now.

@aweary
Copy link
Collaborator Author

aweary commented Aug 23, 2017

@ljharb @lelandrichardson I think this is ready for a full review. I marked all the issues this should resolve in the top comment and added tests for them.

return nodeHasProperty(node, token.name, token.value);
case PSEUDO_ELEMENT:
case PSEUDO_CLASS:
throw new Error('Enzyme::Selector does not support psuedo-element or psuedo-class selectors.');
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding support for pseudo-selectors will be really easy. For example, implementing :not (#460). I figure this can be done in a follow-up.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For reference, I implemented :not with this same approach here: https://github.com/aweary/react-tester/blob/master/src/selector-parser.js#L91-L102

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Followups for these things are great; this PR's big enough already :-)

@aweary aweary changed the title [WIP] Integrate with a CSS parser for selector parsing Integrate with a CSS parser for selector parsing Aug 23, 2017
* @example '[data-foo=foo]' matches <div data-foo="foo" />
*/
case ATTRIBUTE_VALUE:
return nodeHasProperty(node, token.name, token.value);
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just realized the parser returns the operator used in attribute value selectors, so we can now support selectors like [foo|="bar"] and [foo^="bar"]

@aweary
Copy link
Collaborator Author

aweary commented Aug 29, 2017

@ljharb do you think we'll be able to get this in for v3?

Copy link
Member

@ljharb ljharb left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, this is looking great.

Aside from the breaking change of our helper functions, is there anything in the main shallow/mount API that used to work, but will stop working as a result of this change? If so, we'll need to include it in our v3 migration guide.

});

it('throws for malformed selectors', () => {
expect(() => fn('div[data-name="xyz"')).to.throw(/Enzyme::Selector received what appears to be a malformed string selector/);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does this no longer throw (somewhere)? it's a malformed selector.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It does throw, but since the new rst-selector-parser manages all parsing this should be covered by the unit tests for the package. I figure at this point we'd be testing the behavior of a dependency with its own tests.

But I'll add back a test that ensures that we catch the general parsing error and re-throw something more specific. Then I think it's safe to assume invalid selectors will throw, and if the rst-selector-parser coverage is missing a case we can add it there.

On that note, I'd be happy to include rst-selector-parser as part of this monorepo if we want to.

it('should parse false as a literal', () => {
const node = $(<div foo />);

expect(nodeHasProperty(node, 'foo', 'true')).to.equal(true);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should this test not still be valid for true instead of 'true'?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It should, I'll add that case.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

expect(nodeHasProperty(<div foo={2} />, 'foo', '-0')).to.equal(false);
expect(nodeHasProperty(<div foo={0} />, 'foo', 0)).to.equal(true);
expect(nodeHasProperty(<div foo={0} />, 'foo', +0)).to.equal(true);
expect(nodeHasProperty(<div foo={-0} />, 'foo', -0)).to.equal(true);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we add test cases that -0 does not match 0, and 0 does not match -0?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

expect(nodeHasProperty(<div foo={0} />, 'foo', +0)).to.equal(true);
expect(nodeHasProperty(<div foo={-0} />, 'foo', -0)).to.equal(true);
expect(nodeHasProperty(<div foo={1} />, 'foo', 0)).to.equal(false);
expect(nodeHasProperty(<div foo={2} />, 'foo', -0)).to.equal(false);
});

it('should work with empty strings', () => {
expect(nodeHasProperty(<div foo={''} />, 'foo', '')).to.equal(true);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for funsies, let's add foo="" also?


expect(() => nodeHasProperty(node, 'title', 'foo')).to.throw();
expect(nodeHasProperty(<div foo={Infinity} />, 'foo', Infinity)).to.equal(true);
expect(nodeHasProperty(<div foo={Infinity} />, 'foo', +Infinity)).to.equal(true);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's add a case where Infinity fails to match a prop value of Infinity?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It'd also be good to ensure NaN and Infinity don't equate, since both are not finite

* @param {Token} token
*/
function nodeMatchesToken(node, token) {
if (node === null || typeof node === 'string') {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you elaborate on why a string node wouldn't match? is there no selector that can match text?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct, with the current grammar there's no way to match a string literal. foo would match an element of type foo. We could augment the grammar to allow something like "foo" but I'm not sure how useful that really is.

return nodeHasProperty(node, token.name, token.value);
case PSEUDO_ELEMENT:
case PSEUDO_CLASS:
throw new Error('Enzyme::Selector does not support psuedo-element or psuedo-class selectors.');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Followups for these things are great; this PR's big enough already :-)

* @param {Function|Object|String} selector
*/
export function buildPredicate(selector) {
switch (typeof selector) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fwiw, this bypasses the engine optimization when typeof foo is repeated. This would be better as an if/else, for a number of reasons.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ljharb can you elaborate on what optimization this bypasses? I'm not familiar.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

engines are able to do typeof foo === 'some string' checks very very fast; when you store typeof foo in a variable (or a binding, as here) it has to go the slower path.

// constructor
return node => node && node.type === selector;
case 'object':
if (!Array.isArray(selector) && selector !== null && !isEmpty(selector)) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what does lodash return for isEmpty(null)?

if we're keeping !== null for perf, then we should be checking that first, before isArray.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll figure out what it returns and update this. I just copied what was already there FWIW: https://github.com/airbnb/enzyme/blob/c15dd479684a9e3423d3efb5aea592fcfde1a842/packages/enzyme/src/RSTTraversal.js#L119

export function reduceTreeBySelector(selector, wrapper) {
const root = wrapper.getNodeInternal();
let results = [];
switch (typeof selector) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here wrt typeof and if/else

Copy link
Collaborator Author

@aweary aweary left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ljharb thanks! I'll move those changes today.

I think this should be backward compatible. There may be breaking changes if we were silently incorrectly parsing and applying a selector before, but as far as expected behavior goes it should be consistent.

});

it('throws for malformed selectors', () => {
expect(() => fn('div[data-name="xyz"')).to.throw(/Enzyme::Selector received what appears to be a malformed string selector/);
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It does throw, but since the new rst-selector-parser manages all parsing this should be covered by the unit tests for the package. I figure at this point we'd be testing the behavior of a dependency with its own tests.

But I'll add back a test that ensures that we catch the general parsing error and re-throw something more specific. Then I think it's safe to assume invalid selectors will throw, and if the rst-selector-parser coverage is missing a case we can add it there.

On that note, I'd be happy to include rst-selector-parser as part of this monorepo if we want to.

it('should parse false as a literal', () => {
const node = $(<div foo />);

expect(nodeHasProperty(node, 'foo', 'true')).to.equal(true);
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It should, I'll add that case.

@@ -433,22 +435,15 @@ describeWithDOM('mount', () => {
expect(wrapper.find('.row + .row')).to.have.lengthOf(1);
});

// React 15.2 warns when setting a non valid prop to an DOM element
describeIf(REACT013 || REACT014, 'unauthorized dom props', () => {
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, OK to do in a follow up?

@@ -39,6 +39,7 @@
"object.assign": "^4.0.4",
"object.entries": "^1.0.4",
"raf": "^3.3.2",
"rst-selector-parser": "^2.2.0",
"uuid": "^3.1.0"
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes! I'll remove it.

* @param {Token} token
*/
function nodeMatchesToken(node, token) {
if (node === null || typeof node === 'string') {
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct, with the current grammar there's no way to match a string literal. foo would match an element of type foo. We could augment the grammar to allow something like "foo" but I'm not sure how useful that really is.

// constructor
return node => node && node.type === selector;
case 'object':
if (!Array.isArray(selector) && selector !== null && !isEmpty(selector)) {
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll figure out what it returns and update this. I just copied what was already there FWIW: https://github.com/airbnb/enzyme/blob/c15dd479684a9e3423d3efb5aea592fcfde1a842/packages/enzyme/src/RSTTraversal.js#L119

@aweary
Copy link
Collaborator Author

aweary commented Sep 2, 2017

@ljharb I think I've addressed all of your feedback!

it('simple adjacent', () => {
const wrapper = renderMethod(
<div>
<div className="to-find" />
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

something worth testing is to have children of a node that are in separate arrays but adjacent to one another:

<div>
  <div className="a" />
  {[<div key="0" className="b" />]}
</div>

With this you can assert:

expect(wrapper.find('.a + .b'))..to.have.lengthOf(1)

* @param {*} root
* @param {*} targetNode
*/
export function findParentNode(root, targetNode) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just curious.... RSTTraversal.js has a parentsOfNode function that you could potentially use for this? Did it not work when you tried or did you not know about it?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did see that, and it wasn't working for me. I didn't spend a lot of time trying to fix it though since I wasn't really following how it was implemented. I'm not actually sure how it's working at all, since it calls reverse() on the result of pathToNode, but pathToNode returns null, which is the error I was getting

TypeError: Cannot read property 'reverse' of null
      at parentsOfNode (packages/enzyme/build/RSTTraversal.js:121:10)

It's also more complicated than we need for selector matching since AFAICT it finds all parents, not just the direct parent. If we add a parent pointer per discussions in #1085 this utility could go away altogether.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that that error is a pre-existing bug in enzyme 2 - #410

* by scalpel.
* @param {String} selector
*/
function safelyGenerateTokens(selector) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

feels weird to call this safely{thing} but still have it throw

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nah throwing is the safest thing ever! Unsafe things are what don't throw :-)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I was thinking "safe" because we explicitly throw our own error instead of letting the parser crash, and know that the tokens don't require additional validation.

Copy link
Collaborator

@lelandrichardson lelandrichardson left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wow this PR is SOOOO GOOOD. Thank you so much for doing this. Looks like it was a ton of work to do right but this is so much better than what we had. I had a couple of minor comments, but nothing blocking. Once Jordan signs off, let's get this in!

@lelandrichardson
Copy link
Collaborator

Oh, also, sorry this took me so long to look at!

Copy link
Member

@ljharb ljharb left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM once the remaining comments have been addressed

expect(nodeHasProperty(<div foo={null} />, 'foo', 'null')).to.equal(true);
expect(nodeHasProperty(<div foo={0} />, 'foo', 'null')).to.equal(false);
expect(nodeHasProperty(<div foo={null} />, 'foo', null)).to.equal(true);
expect(nodeHasProperty(<div foo={0} />, 'foo', null)).to.equal(false);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's also add a pair of assertions that null won't equal undefined?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the expected behavior with undefined? It looks like it returns whether nodeProps has an own property matching the name, which means undefined ends up matching since foo exists.

Is that expected?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nodeHasProperty(<div foo={undefined} />, 'foo', undefined) should be true, nodeHasProperty(<div foo={undefined} />, 'foo', null) should be false, nodeHasProperty(<div foo={null} />, 'foo', undefined) should be false.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right now nodeHasProperty(<div foo={null} />, 'foo', undefined) returns true because it's defaulting to the own-property check. I think this behavior existed before this PR too, is that a bug? I'm not sure if that will break some existing tests.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ping @ljharb ^ I think that's the last point of clarification I need before this is good to go.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the interest of time, i'm going to move forward w/ this PR. I've verified that this is the current behavior. If we would like to clarify it / change it in an upcoming PR, i'm fine with that. @aweary thanks for being so thorough here.


expect(() => nodeHasProperty(node, 'title', 'foo')).to.throw();
expect(nodeHasProperty(<div foo={Infinity} />, 'foo', Infinity)).to.equal(true);
expect(nodeHasProperty(<div foo={Infinity} />, 'foo', +Infinity)).to.equal(true);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It'd also be good to ensure NaN and Infinity don't equate, since both are not finite

@@ -433,22 +435,15 @@ describeWithDOM('mount', () => {
expect(wrapper.find('.row + .row')).to.have.lengthOf(1);
});

// React 15.2 warns when setting a non valid prop to an DOM element
describeIf(REACT013 || REACT014, 'unauthorized dom props', () => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, totes ok to do in a followup

@aweary
Copy link
Collaborator Author

aweary commented Sep 13, 2017

@lelandrichardson a quick heads up, to get the tests passing I had to make a change to the React 16 adapter (a79e150) since the internal instance key was changed in facebook/react@9921e57

@lelandrichardson
Copy link
Collaborator

@aweary sounds good. thanks.

@aweary
Copy link
Collaborator Author

aweary commented Sep 13, 2017

I think I've addressed all the feedback, with the exception of my question here #1086 (comment) about nodeHasProperty behavior. Karma tests are failing on CI, trying to reproduce that locally now.

@aweary
Copy link
Collaborator Author

aweary commented Sep 13, 2017

It looks like the Karma tests are failing intermittently. I restarted the build a couple times on Travis and it eventually worked 🤷‍♂️

@lelandrichardson
Copy link
Collaborator

@aweary do you have a link to a build where it failed? or the error message?

@aweary
Copy link
Collaborator Author

aweary commented Sep 14, 2017

@lelandrichardson the last build had the failure with the karma tests and React 16: https://travis-ci.org/airbnb/enzyme/jobs/274910583

It didn't mark the whole build as failed since it's considered an allowed failure, but that's the same error I was seeing before. It looks like some kind of timeout issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants